package org.sction.support.spring;

import java.lang.reflect.Field;

import javax.annotation.Resource;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class SpringContextHolder implements ApplicationContextAware,
		DisposableBean {

	private static ApplicationContext applicationContext = null;
	private static Logger logger = Logger.getLogger(SpringContextHolder.class);

	public static Object getBean(Field field) {
		if (field.isAnnotationPresent(Resource.class)) {
			Resource r = field.getAnnotation(Resource.class);
			String name = r.name();
			if (name == null || name.equals("")) {
				name = field.getName();
			}
			if (applicationContext.getBean(name) != null) {
				return applicationContext.getBean(name);
			}
		} else if (field.isAnnotationPresent(Autowired.class)) {
			String[] names = applicationContext.getBeanNamesForType(field
					.getType());
			if (names == null || names.length <= 0) {
				return applicationContext.getBean(field.getName());
			} else {
				for (String name : names) {
					if (applicationContext.getBean(name) != null) {
						return applicationContext.getBean(name);
					}
				}
			}
		}
		return null;
	}

	public void setApplicationContext(ApplicationContext applicationContext) {
		logger.debug("注入ApplicationContext到SpringContextHolder:"
				+ applicationContext);
		if (SpringContextHolder.applicationContext != null) {
			logger.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:"
					+ SpringContextHolder.applicationContext);
		}
		SpringContextHolder.applicationContext = applicationContext; // NOSONAR
	}

	@Override
	public void destroy() throws Exception {
		SpringContextHolder.clear();
	}

	public static ApplicationContext getApplicationContext() {
		assertContextInjected();
		return applicationContext;
	}

	@SuppressWarnings("unchecked")
	public static <T> T getBean(String name) {
		assertContextInjected();
		return (T) applicationContext.getBean(name);
	}

	public static <T> T getBean(Class<T> requiredType) {
		assertContextInjected();
		return applicationContext.getBean(requiredType);
	}

	public static <T> T getBean(String name, Class<T> requiredType) {
		assertContextInjected();
		return applicationContext.getBean(name, requiredType);
	}

	public static void clear() {
		logger.debug("清除SpringContextHolder中的ApplicationContext:"
				+ applicationContext);
		applicationContext = null;
	}

	private static void assertContextInjected() {
		if (applicationContext == null) {
			throw new IllegalStateException(
					"applicaitonContext未注入,请在applicationContext.xml中定义SpringContextHolder");
		}
	}

	public static void registerBean(Class<?> clazz, String name,
			boolean singleton) {
		assertContextInjected();
		DefaultListableBeanFactory acf = (DefaultListableBeanFactory) applicationContext
				.getAutowireCapableBeanFactory();

		if (!acf.containsBean(name)) {
			String scope = singleton ? "singleton" : "prototype";
			BeanDefinitionBuilder bdb = BeanDefinitionBuilder
					.rootBeanDefinition(clazz);
			bdb.setScope(scope);
			logger.info("RegisterSpringBean :" + name + "<" + clazz.getName()
					+ ">(" + scope + ")");
			acf.registerBeanDefinition(name, bdb.getBeanDefinition());
		} else {
			BeanDefinition bd = acf.getBeanDefinition(name);
			logger.info("RemoveSpringBean :" + name + "<"
					+ bd.getBeanClassName() + ">(" + bd.getScope() + ")");
			acf.removeBeanDefinition(name);

			String scope = singleton ? "singleton" : "prototype";
			BeanDefinitionBuilder bdb = BeanDefinitionBuilder
					.rootBeanDefinition(clazz);
			bdb.setScope(scope);
			logger.info("RegisterSpringBean :" + name + "<" + clazz.getName()
					+ ">(" + scope + ")");
			acf.registerBeanDefinition(name, bdb.getBeanDefinition());
		}
	}

	public static void removeBean(String name) {
		assertContextInjected();
		DefaultListableBeanFactory acf = (DefaultListableBeanFactory) applicationContext
				.getAutowireCapableBeanFactory();
		if (acf.containsBean(name)) {
			BeanDefinition bd = acf.getBeanDefinition(name);
			logger.info("RemoveSpringBean :" + name + "<"
					+ bd.getBeanClassName() + ">(" + bd.getScope() + ")");
			acf.removeBeanDefinition(name);
		}
	}
}
